import 'dart:math';

import 'package:flutter/material.dart';
import 'package:kq_flutter_core_widget/utils/ex/kq_ex.dart';
import 'package:kq_flutter_core_widget/widgets/chart/axis/horizontal_bar/horizontal_bar_chart.dart';
import 'package:kq_flutter_pad_widgets/widgets/chart/pad_chart/axis/pad_x_axis_render.dart';
import 'package:kq_flutter_pad_widgets/widgets/chart/pad_chart/axis/pad_y_axis_render.dart';

import '../../base/pad_base_chart.dart';
import '../bar/kq_pad_bar_chart.dart';
import '../base/pad_base_axis_chart.dart';

/// 横向柱状图代理
///
/// Created by wanggaowan on 2023/12/1 13:22
class KqPadHorizontalBarChartDelegate
    extends PadBaseAxisChartDelegate<HorizontalBarData> {
  /// 构建柱状图
  ///
  /// [xAxis]配置x轴相关属性，[xAxisRender]则负责x轴的绘制。
  /// [yAxis]配置y轴相关属性，[yAxisRender]则负责y轴的绘制。
  /// [dataRender]负责将柱状图数据绘制到界面及高亮数据绘制。
  /// [gestureHandler]为手势处理器，处理图表的点击，滑动等操作。
  /// [emptyWidgetBuilder]为无数据时构建需要显示的内容。
  /// [isEmptyData]用于判断给得的数据是否为空，从而决定是否调用[emptyWidgetBuilder]绘制空数据界面。
  /// [animDuration]为动画时间，此值大于0则绘制时伴随动画
  KqPadHorizontalBarChartDelegate(
      {PadHorizontalBarChartDataRender? dataRender,
      HorizontalXAxis? xAxis,
      HorizontalYAxis? yAxis,
      PadXAxisRender? xAxisRender,
      PadYAxisRender? yAxisRender,
      super.animDuration,
      super.data,
      PadHorizontalBarCharGestureHandler? gestureHandler,
      super.emptyWidgetBuilder,
      super.isDataEmpty})
      : super(
          dataRender: dataRender ?? const PadHorizontalBarChartDataRender(),
          xAxis: xAxis ?? HorizontalXAxis(),
          yAxis: yAxis ?? HorizontalYAxis(),
          xAxisRender: xAxisRender ?? PadXAxisRender(),
          yAxisRender: yAxisRender ?? PadYAxisRender(isDrawLabelByGridLine: false),
          gestureHandler: gestureHandler,
        );

  /// 构建柱状图，对非必传的功能提供默认实现
  ///
  /// [xAxis]配置x轴相关属性，[xAxisRender]则负责x轴的绘制。
  /// [yAxis]配置y轴相关属性，[yAxisRender]则负责y轴的绘制。
  /// [dataRender]负责将柱状图数据绘制到界面及高亮数据绘制。
  /// [gestureHandler]为手势处理器，处理图表的点击，滑动等操作。
  /// [emptyWidgetBuilder]为无数据时构建需要显示的内容。
  /// [isEmptyData]用于判断给得的数据是否为空，从而决定是否调用[emptyWidgetBuilder]绘制空数据界面。
  /// [animDuration]为动画时间，此值大于0则绘制时伴随动画
  KqPadHorizontalBarChartDelegate.withDefault(
      {HorizontalXAxis? xAxis,
      HorizontalYAxis? yAxis,
      PadHorizontalBarChartDataRender? dataRender,
      PadXAxisRender? xAxisRender,
      PadYAxisRender? yAxisRender,
      Duration? animDuration,
      HorizontalBarData? data,
      PadHorizontalBarCharGestureHandler? gestureHandler,
      Widget Function()? emptyWidgetBuilder,
      bool Function(HorizontalBarData? data)? isDataEmpty,
      HorizontalBarChartHighLightRender? highLightRender})
      : this(
            xAxis: xAxis,
            yAxis: yAxis,
            dataRender: dataRender,
            xAxisRender: xAxisRender,
            yAxisRender: yAxisRender,
            animDuration: animDuration,
            data: data,
            gestureHandler: gestureHandler ?? PadHorizontalBarCharGestureHandler(),
            emptyWidgetBuilder: emptyWidgetBuilder ?? getDefEmptyView,
            isDataEmpty: isDataEmpty);

  @override
  PadHorizontalBarCharGestureHandler? get gestureHandler =>
      super.gestureHandler as PadHorizontalBarCharGestureHandler?;

  @override
  HorizontalXAxis get xAxis => super.xAxis as HorizontalXAxis;

  @override
  HorizontalYAxis get yAxis => super.yAxis as HorizontalYAxis;

  @override
  bool get isEmptyData {
    var isDataEmptyFunc = isDataEmpty;
    if (isDataEmptyFunc != null) {
      return isDataEmptyFunc.call(data);
    }

    return data == null;
  }

  @override
  double getGridHeight(int labelCount, double yAxisLength) {
    return yAxisLength / yAxis.labelCount;
  }
}

/// 柱状图手势处理器
class PadHorizontalBarCharGestureHandler extends PadBaseAxisChartGestureHandler<
    KqPadHorizontalBarChartDelegate, List<BarEntity>> {
  PadHorizontalBarCharGestureHandler(
      {super.tapEnable,
      super.dragEnable,
      super.onTap,
      super.touchData});

  @override
  void onDragUpdate(DragUpdateDetails details) {
    if (details.delta.dy.abs() < details.delta.dx.abs()) {
      return;
    }

    if (chart == null || !dragEnable) {
      return;
    }

    findTouchInRange(details.localPosition, true);
  }

  @override
  List<BarEntity>? getTouchData(Offset offset, bool isMove) {
    var chart = super.chart;
    if (chart == null) {
      return null;
    }

    var data = chart.data;
    if (data == null || data.groups.isNullOrEmpty) {
      return null;
    }

    var list = <BarEntity>[];
    for (var entity in data.groups!) {
      for (var value in entity.values) {
        if (_isTouchInRectF(offset, value.drawRect, isMove)) {
          list.add(value);
          break;
        }
      }
    }
    return list.isEmpty ? null : list;
  }

  /// 点击是否在指定范围内容，[isYAllValid]表示是否y轴全局域触摸有效，如果为true，那么则无需判断手指y轴坐标
  bool _isTouchInRectF(Offset offset, Rect rect, bool isYAllValid) {
    if (offset.dy >= rect.top && offset.dy <= rect.bottom) {
      if (isYAllValid) {
        return true;
      }

      return offset.dx >= rect.left && offset.dx <= rect.right;
    }

    return false;
  }
}

const Color _maskColor = Color(0x55000000);

class HorizontalBarChartHighLightRender {
  const HorizontalBarChartHighLightRender(
      {this.maskColor = _maskColor, this.labelBuilder, this.labelOffset = 2});

  /// 高亮时柱状图的遮罩颜色
  final Color maskColor;

  /// 构建高亮要绘制的文本内容
  final HighLightLabel? Function(BarEntity data)? labelBuilder;

  /// 标签与柱形图的间距
  final double labelOffset;

  /// 绘制高亮内容，此时[BarEntity.drawRect]已就绪
  ///
  /// [canvas] 画布
  ///
  /// [rect] 水平可绘制区域。
  ///
  /// rect.left:起始点坐标 = x轴起始点 + [XAxis.startPadding]。
  ///
  /// rect.top:可绘制区域顶部坐标 = y轴顶部 - [YAxis.endPadding]。
  ///
  /// rect.right:水平可绘制区域结束点坐标 = x轴结束点 - [XAxis.endPadding]。
  ///
  /// rect.bottom:可绘制区域底部坐标 = y轴底部 + [YAxis.startPadding]。
  void onDraw(KqPadHorizontalBarChartDelegate chart, Canvas canvas, Rect rect,
      List<BarEntity> data) {
    var paint = Paint();
    paint.color = maskColor;
    paint.style = PaintingStyle.fill;
    var textPaint = TextPainter(textDirection: TextDirection.ltr);
    for (var value in data) {
      canvas.drawRect(value.drawRect, paint);
      var label = labelBuilder?.call(value);
      if (label != null) {
        _drawText(chart, canvas, rect, value, label.text, label.color,
            label.size, textPaint, false, labelOffset);
      }
    }
    textPaint.dispose();
  }
}

void _drawText(
    KqPadHorizontalBarChartDelegate chart,
    Canvas canvas,
    Rect rect,
    BarEntity data,
    String text,
    Color color,
    double size,
    TextPainter paint,
    bool fixTop,
    double labelOffset) {
  if (text.isEmpty) {
    return;
  }

  var textStyle = TextStyle(fontSize: size, color: color);

  paint.text = TextSpan(
    text: text,
    style: textStyle,
  );

  var maxWidth = rect.right - data.drawRect.right;
  double drawX;
  if (!fixTop && maxWidth > 0) {
    paint.textAlign = TextAlign.start;
    paint.layout(maxWidth: maxWidth, minWidth: maxWidth);
    if (paint.height > data.drawRect.height) {
      drawX = rect.left;
      paint.textAlign = TextAlign.end;
      paint.layout(maxWidth: rect.width, minWidth: rect.width);
      if (paint.computeLineMetrics().length > 1) {
        paint.textAlign = TextAlign.start;
        paint.layout(maxWidth: rect.width, minWidth: rect.width);
      }
    } else {
      drawX = data.drawRect.right + data.labelOffset;
    }
  } else {
    drawX = rect.left;
    paint.textAlign = TextAlign.end;
    paint.layout(maxWidth: rect.width, minWidth: rect.width);
    if (paint.computeLineMetrics().length > 1) {
      paint.textAlign = TextAlign.start;
      paint.layout(maxWidth: rect.width, minWidth: rect.width);
    }
  }

  double drawY = max(0, (data.drawRect.height - paint.height) / 2);
  paint.paint(canvas, Offset(drawX, data.drawRect.top + drawY));
}

class PadHorizontalBarChartDataRender
    with PadBaseDataRenderMixin<KqPadHorizontalBarChartDelegate> {
  const PadHorizontalBarChartDataRender({this.highLightRender});

  final HorizontalBarChartHighLightRender? highLightRender;

  @override
  void onDraw(KqPadHorizontalBarChartDelegate chart, Canvas canvas, Rect rect,
      double gridWidth, double gridHeight, double animProgress) {
    var data = chart.data;
    if (data == null) {
      return;
    }

    var paint = Paint();
    paint.style = PaintingStyle.fill;

    var groups = data.groups;
    if (groups != null && groups.isNotEmpty) {
      drawBar(chart, canvas, groups, rect, gridHeight, animProgress, paint);

      var textPaint = TextPainter(textDirection: TextDirection.ltr);
      for (var value in groups) {
        for (var value1 in value.values) {
          if (value1.drawLabel) {
            _drawText(
                chart,
                canvas,
                rect,
                value1,
                value1.label ?? '',
                value1.labelColor,
                value1.labelTextSize,
                textPaint,
                value1.labelFixTop,
                value1.labelOffset);
          }
        }
      }
      textPaint.dispose();
    }

    if (animProgress >= 1) {
      var touchData = chart.gestureHandler?.touchData;
      if (touchData != null && touchData.isNotEmpty) {
        highLightRender?.onDraw(chart, canvas, rect, touchData);
      }
    }
  }

  /// 绘制柱状图
  @protected
  void drawBar(
      KqPadHorizontalBarChartDelegate chart,
      Canvas canvas,
      List<HorizontalBarGroupEntity> dataList,
      Rect rect,
      double gridHeight,
      double animProgress,
      Paint paint) {
    var xAxis = chart.xAxis;
    var tempBottom = rect.bottom;
    var range = rect.width;
    var maxValue = xAxis.max - xAxis.min;

    for (var data in dataList) {
      if (data.values.isEmpty) {
        continue;
      }

      double totalPercent = data.values.fold(
          0, (previousValue, element) => previousValue + element.widthPercent);

      var height = gridHeight - data.barOffset * (data.values.length - 1);
      double drawBottom;
      if (totalPercent >= 1 || data.gravity == HorizontalBarGravity.bottom) {
        drawBottom = tempBottom;
      } else if (data.gravity == HorizontalBarGravity.top) {
        drawBottom = tempBottom - height * (1 - totalPercent);
      } else {
        drawBottom = tempBottom - height * (1 - totalPercent) / 2;
      }

      for (var entity in data.values) {
        var barHeight = entity.widthPercent * height;
        var alpha = entity.bgColor.alpha;
        if (alpha > 0) {
          var rect2 = Rect.fromLTRB(
              rect.left, drawBottom - barHeight, rect.right, drawBottom);
          paint.color = entity.bgColor;
          paint.shader = null;
          canvas.drawRect(rect2, paint);
        }

        var barWidth =
            (entity.value - xAxis.min) / maxValue * range * animProgress;
        entity.$drawRect = Rect.fromLTRB(rect.left, drawBottom - barHeight,
            rect.left + barWidth, drawBottom);
        if (entity.barColors.length > 1) {
          paint.shader = LinearGradient(colors: entity.barColors)
              .createShader(entity.drawRect);
        } else {
          paint.shader = null;
        }
        paint.color = entity.barColors[0];
        canvas.drawRect(entity.drawRect, paint);

        drawBottom -= barHeight + data.barOffset;
      }

      tempBottom -= gridHeight;
    }
  }
}
